package com.utils.card;

import java.util.*;

public class RuleUtil {

    public static final String ERROR = "error";

    public static final String Single = "Single";
    public static final String Pair = "Pair";
    public static final String TripleByNull = "TripleByNull";
    public static final String TripleByOne = "TripleByOne";
    public static final String TripleByPair = "TripleByPair";
    public static final String Bomb = "Bomb";
    public static final String Continuous = "Continuous";
    public static final String PairContinuous = "PairContinuous";
    public static final String AirplaneByNull = "AirplaneByNull"; //飞机不带
    public static final String AirplaneByOne = "AirplaneByOne"; //飞机带单
    public static final String AirplaneByPair = "AirplaneByPair"; //飞机带一对

    private static String[] allCard = {"B", "X", "2", "A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3"};

    /**
     * 移除自己的牌
     */
    public static void removeCard(String info, ArrayList<String> myCards) {
        String[] split = info.split("");
        for (String card : split) {
            myCards.remove(card);
        }
    }

    private static boolean checkRepeatCard(String[] sp, ArrayList<String> myCards) {
        HashMap<String, Integer> map = new HashMap<>();
        for (String str : sp) {
            if (!map.containsKey(str)) {
                map.put(str, 1);
            } else {
                Integer count = map.get(str);
                map.put(str, ++count);
            }
        }
        HashMap<String, Integer> map2 = new HashMap<>();
        for (String myCard : myCards) {
            if (!map2.containsKey(myCard)) {
                map2.put(myCard, 1);
            } else {
                Integer count = map2.get(myCard);
                map2.put(myCard, ++count);
            }
        }

        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            Integer value1 = map.get(key);
            Integer value2 = map2.get(key);
            if (value1 > value2) {
                //找到了我发牌信息带的数量小于自己牌组中不一致则不合法
                return true;
            }
        }
        return false;

    }


    /**
     * 检查牌的类型
     */
    public static String checkCard(String info, ArrayList<String> myCards) {
        //这里需要对info的牌面进行排序，不过后续功能做完后再写也不迟...


        String[] sp = info.split("");
        for (String card : sp) {
            if (!myCards.contains(card)) {
                return ERROR;
            }
        }
        //考虑重复的牌
        if (checkRepeatCard(sp, myCards)) {
            return ERROR;
        }


        int len = info.length();
        String result;
        switch (len) {
            case 0:
                result = ERROR;
                break;
            case 1:
                result = checkSingle(info, myCards);
                break;
            case 2:
                result = checkTwoCards(info.split(""));
                break;
            case 3:
                result = checkThreeCards(info.split(""));
                break;
            case 4:
                result = checkFourCards(info.split(""));
                break;
            case 5:
                result = checkFiveCards(info.split(""));
                break;
            default:
                result = defaultInspection(info.split(""));
                break;

        }
        return result;
    }


    /**
     * 比较牌面大小
     */
    public static boolean compare(String myCard, String lastCard) {
        //有可能我出个炸弹4444 比上一个牌的第一个牌小，走特殊判断
        String[] split = myCard.split("");
        String[] split2 = lastCard.split("");
        if (split.length == 4) {
            String result = checkFourCards(split);
            String result2 = null;
            if (split2.length == 4) {
                result2 = checkFourCards(split2);
            }
            if (split2.length == 2) {
                result2 = checkTwoCards(split2);
            }

            if (Bomb.equals(result) && !Bomb.equals(result2)) {
                //如果我是炸弹，对方的不是，直接返回true！
                return true;
            }
        }


        String my = myCard.substring(0, 1);
        String you = lastCard.substring(0, 1);
        List<String> list = Arrays.asList(allCard);
        int myIndex = list.indexOf(my);
        int youIndex = list.indexOf(you);
        return myIndex < youIndex;
    }


    //下面的私有方法有检查单张牌、两张牌、三张、四张、五张、超过五张的类型，返回String
    private static String checkSingle(String info, ArrayList<String> myCards) {
        List<String> list = Arrays.asList(allCard);
        if (list.contains(info) && myCards.contains(info)) {
            return Single;
        }
        return ERROR;
    }


    private static String checkTwoCards(String[] card) {
        //是否为大小王或对子
        if ("XB".equals(card[0] + card[1]) || "BX".equals(card[0] + card[1])) {
            return Bomb;
        }
        //需要考虑错误情况
        if ("XX".equals(card[0] + card[1]) || "BB".equals(card[0] + card[1])) {
            return ERROR;
        }
        return card[0].equals(card[1]) ? Pair : ERROR;
    }


    private static String checkThreeCards(String[] card) {
        //是否为三张一样的牌
        return card[0].equals(card[1]) && card[0].equals(card[2]) ? TripleByNull : ERROR;
    }


    private static String checkFourCards(String[] card) {
        //至少有两张牌相同
        if (card[0].equals(card[1]) || card[2].equals(card[3])) {
            //至少有三张牌相同,三带一或炸弹
            if (card[1].equals(card[2]) || card[0].equals(card[3])) {
                return card[0].equals(card[3]) ? Bomb : TripleByOne;
            }
        }
        return ERROR;
    }


    /**
     * 对五张卡片进行检查
     *
     * @param card 待检查卡片
     * @return 是否成功
     */
    private static String checkFiveCards(String[] card) {
        if (isContinuousCard(card)) return Continuous;//连牌
        Map<String, Integer> map = new HashMap<>();
        for (String aCard : card) {
            if (!map.containsKey(aCard)) {
                //说明第一次
                map.put(aCard, 1);
            } else {
                Integer count = map.get(aCard);
                map.put(aCard, ++count);
            }
        }
        //是否为三带一对
        return map.size() == 2 && map.containsValue(3) ? TripleByPair : ERROR;
    }


    /**
     * 超过5张牌，连牌、连对，飞机【带，不带】
     *
     * @param card 待检查卡片
     * @return 是否成功
     */
    private static String defaultInspection(String[] card) {
        if (isContinuousCard(card)) {
            return Continuous;//连牌
        }
        int min, max, span;
        ArrayList<String> cards = new ArrayList<>();
        Collections.addAll(cards, card);
        min = max = cards.indexOf(card[0]);
        Map<String, Integer> map = new HashMap<>();
        for (String aCard : card) {
            if (!map.containsKey(aCard)) {
                //说明第一次
                map.put(aCard, 1);
            } else {
                Integer count = map.get(aCard);
                map.put(aCard, ++count);
            }

            //tmp = cards.indexOf(aCard);
            if (cards.indexOf(aCard) < min) {
                min = cards.indexOf(aCard);
            }
            if (cards.indexOf(aCard) > max) {
                max = cards.indexOf(aCard);
            }
        }
        span = change(card[max]) - change(card[min]) + 1;
        if (map.containsValue(4)) {
            return ERROR;//规定牌不能连炸
        }
        if (isContinuousPairCard(card, map, span)) {
            return PairContinuous;//连队
        }
        return isAirplaneCard(card, map, span);
    }


    /**
     * 判断是否为连队
     */
    private static boolean isContinuousPairCard(String[] card, Map<String, Integer> map, int span) {
        if (card.length % 2 != 0) return false;//不是偶数
        ArrayList<String> cards = new ArrayList<>();
        Collections.addAll(cards, card);
        //这几张牌不能组成连牌
        if (cards.contains("2") || cards.contains("X") || cards.contains("B")) {
            return false;
        }
        //长度等于数量的一半，且不包含3或4张相同的牌，则所有牌均为一对 如：33445566
        return card.length / 2 == span && !map.containsValue(3);
    }


    /**
     * 判断是否为飞机
     */
    private static String isAirplaneCard(String[] card, Map<String, Integer> map, int span) {
        //不带牌333444555
        if (card.length == span * 3) {
            return AirplaneByNull;
        }
        int min, max, tmp;
        min = max = tmp = -1;
        ArrayList<String> cards = new ArrayList<>();
        Collections.addAll(cards, card);
        Set<String> set = map.keySet();
        for (String key : set) {
            if (map.get(key) == 3) {
                if (tmp == -1) {
                    max = min = tmp = cards.indexOf(key);
                } else {
                    tmp = cards.indexOf(key);
                    if (tmp < min) {
                        min = tmp;
                    } else if (tmp > max) {
                        max = tmp;
                    }
                }
            }
        }
        max = change(card[max]);
        min = change(card[min]);
        //带一张单牌333444555678
        if (card.length == (max - min + 1) * 4) {
            return AirplaneByOne;
        }
        //带一对牌333444555667788
        return card.length == (max - min + 1) * 5 && !map.containsValue(1) ? AirplaneByPair : ERROR;
    }


    /**
     * 判断是否为顺子
     *
     * @param card 待检查卡片
     * @return 是否成功
     */
    private static boolean isContinuousCard(String[] card) {
        int min, max;
        ArrayList<String> cards = new ArrayList<>();
        Collections.addAll(cards, card);
        //这几张牌不能组成连牌
        if (cards.contains("2") || cards.contains("X") || cards.contains("B")) {
            return false;
        }
        //但是有个大前提：要求用户一定是按顺序大小 例如 34567
        min = change(card[0]);
        max = change(card[card.length - 1]);
        return card.length == max - min + 1;//最大值减最小值是否等于长度
    }


    /**
     * 将牌面转换数字，计算span
     */
    private static int change(String card) {
        int i = 0;
        //大王小王用不了span，span是在计算飞机和连对才需要，以防万一有牌面返回负数即可
        if ("X".equals(card)) {
            i = -2;
        }
        if ("B".equals(card)) {
            i = -1;
        }
        if ("T".equals(card)) {
            i = 10;
        }
        if ("J".equals(card)) {
            i = 11;
        }
        if ("Q".equals(card)) {
            i = 12;
        }
        if ("K".equals(card)) {
            i = 13;
        }
        if ("A".equals(card)) {
            i = 14;
        }
        if ("2".equals(card)) {
            i = 15;
        }
        if (0 == i) {
            i = Integer.parseInt(card);
        }
        return i;
    }

}
